//! Basic parsing tests for NRC compiler

use nrc::error::CompilerError;
use nrc::generic;
use nrc::*;

#[test]
fn test_lexer_basic() {
    // NRC uses "func" keyword, not "fn"
    let source = "func main() { return 42; }".to_string();
    let lexer = nrc::lexer::Lexer::new(source);

    let tokens: Result<Vec<_>, _> = lexer.collect();
    assert!(tokens.is_ok());

    let tokens = tokens.unwrap();
    assert!(!tokens.is_empty());
}

#[test]
fn test_parser_basic() {
    // NRC uses "func" keyword, not "fn"
    let source = "func main() { return 42; }".to_string();
    let mut parser = nrc::parser::Parser::new(source, Some("test.n".to_string()));

    let program = parser.parse_program();
    assert!(program.is_ok());

    let program = program.unwrap();
    assert!(!program.declarations.is_empty());
}

#[test]
fn test_compiler_creation() {
    let config = CompilerConfig::default();
    let _compiler = Compiler::new(config);

    // Basic test that compiler can be created
    assert!(true);
}

#[test]
fn test_error_handling() {
    let err = CompilerError::lexical(1, 5, "Test error");
    assert!(matches!(err, CompilerError::Lexical { .. }));
}

#[test]
fn test_ast_creation() {
    use nrc::ast::*;
    use nrc::error::Location;

    let location = Location::new(1, 1, 0);
    let func_decl = FunctionDecl {
        name: "test".to_string(),
        generics: vec![],
        parameters: vec![],
        return_type: None,
        body: None,
        attributes: vec![],
        location,
    };

    let decl = Declaration::Function(func_decl);
    assert_eq!(decl.name(), "test");
}

#[test]
fn test_type_system() {
    use nrc::ast::types::*;

    let int_type = Type::Basic(BasicType::Int);
    assert_eq!(int_type.to_string(), "int");

    let array_type = Type::Array(ArrayType {
        element_type: Box::new(int_type),
        size: Some(10),
        location: nrc::error::Location::new(1, 1, 0),
    });

    assert_eq!(array_type.to_string(), "[int, 10]");
}

#[test]
fn test_parse_generic_call_type_arguments() {
    use nrc::ast::{Declaration, Expression, Statement};

    let source = r#"
        func test() {
            foo<int, string>(1);
            obj.method<bool>();
        }
    "#;

    let mut parser = nrc::parser::Parser::new(source.to_string(), None);
    let program = parser.parse_program().expect("program should parse");

    let func = match &program.declarations[0] {
        Declaration::Function(func) => func,
        _ => panic!("expected function declaration"),
    };

    let body = func.body.as_ref().expect("function should have body");
    assert_eq!(body.statements.len(), 2);

    match &body.statements[0] {
        Statement::Expression(Expression::Call(call)) => {
            assert_eq!(call.type_args.len(), 2);
        }
        other => panic!("expected generic function call, got {:?}", other),
    }

    match &body.statements[1] {
        Statement::Expression(Expression::MethodCall(method_call)) => {
            assert_eq!(method_call.type_args.len(), 1);
        }
        other => panic!("expected generic method call, got {:?}", other),
    }
}

#[test]
fn test_parse_generic_struct_declaration() {
    use nrc::ast::Declaration;

    let source = r#"
        type Pair<T> struct {
            value T
        }
    "#;

    let mut parser = nrc::parser::Parser::new(source.to_string(), None);
    let program = parser.parse_program().expect("program should parse");

    match &program.declarations[0] {
        Declaration::Struct(struct_decl) => {
            assert_eq!(struct_decl.name, "Pair");
            assert_eq!(struct_decl.generics.len(), 1);
            assert_eq!(struct_decl.generics[0].name, "T");
            assert_eq!(struct_decl.fields.len(), 1);
            match &struct_decl.fields[0].field_type {
                nrc::ast::types::Type::Generic(name) => assert_eq!(name, "T"),
                other => panic!("expected generic field type, got {:?}", other),
            }
        }
        other => panic!("expected struct declaration, got {:?}", other),
    }
}

#[test]
fn test_parse_where_clause_on_function_generics() {
    use nrc::ast::Declaration;
    use nrc::ast::types::Type;

    let source = r#"
        // Simple marker type used as a bound
        type Marker struct { value int }

        func complex_function<T, U>(t T, u U)
            where T: Marker,
                  U: Marker {
            return
        }
    "#;

    let mut parser = nrc::parser::Parser::new(source.to_string(), None);
    let program = parser.parse_program().expect("program should parse");

    assert_eq!(program.declarations.len(), 2);

    let func = match &program.declarations[1] {
        Declaration::Function(func) => func,
        other => panic!("expected function declaration, got {:?}", other),
    };

    assert_eq!(func.name, "complex_function");
    assert_eq!(func.generics.len(), 2);

    let t_generic = &func.generics[0];
    let u_generic = &func.generics[1];
    assert_eq!(t_generic.name, "T");
    assert_eq!(u_generic.name, "U");

    // where T: Marker  => one constraint
    assert_eq!(t_generic.constraints.len(), 1);
    match &t_generic.constraints[0] {
        Type::Generic(name) => assert_eq!(name, "Marker"),
        other => panic!("expected generic constraint 'Marker', got {:?}", other),
    }

    // where U: Marker  => one constraint
    assert_eq!(u_generic.constraints.len(), 1);
    match &u_generic.constraints[0] {
        Type::Generic(name) => assert_eq!(name, "Marker"),
        other => panic!("expected generic constraint 'Marker', got {:?}", other),
    }
}

#[test]
fn test_parse_where_clause_on_struct_generics() {
    use nrc::ast::Declaration;
    use nrc::ast::types::Type;

    let source = r#"
        type Marker struct { value int }

        type Boxed<T> struct
            where T: Marker {
            inner T
        }
    "#;

    let mut parser = nrc::parser::Parser::new(source.to_string(), None);
    let program = parser.parse_program().expect("program should parse");

    assert_eq!(program.declarations.len(), 2);

    let struct_decl = match &program.declarations[1] {
        Declaration::Struct(s) => s,
        other => panic!("expected struct declaration, got {:?}", other),
    };

    assert_eq!(struct_decl.name, "Boxed");
    assert_eq!(struct_decl.generics.len(), 1);
    let t_generic = &struct_decl.generics[0];
    assert_eq!(t_generic.name, "T");

    assert_eq!(t_generic.constraints.len(), 1);
    match &t_generic.constraints[0] {
        Type::Generic(name) => assert_eq!(name, "Marker"),
        other => panic!("expected generic constraint 'Marker', got {:?}", other),
    }
}

#[test]
fn test_parse_generic_test_file() {
    let source = include_str!("../examples/generic_test.nr");
    let mut parser = nrc::parser::Parser::new(source.to_string(), Some("examples/generic_test.nr".into()));
    let program = parser.parse_program();
    assert!(program.is_ok(), "parser failed: {:?}", program);
}

#[test]
fn test_monomorphize_struct_instantiation() {
    use nrc::ast::types::Type;
    use nrc::ast::{Declaration, Expression, Statement};

    let source = r#"
        type Pair<T> struct {
            value T
        }

        func main() {
            var pair Pair<int> = Pair<int>{ value: 1 }
        }
    "#;

    let mut parser = nrc::parser::Parser::new(source.to_string(), None);
    let mut program = parser.parse_program().expect("program should parse");

    generic::monomorphize_program(&mut program).expect("monomorphization should succeed");

    let struct_names: Vec<String> = program
        .declarations
        .iter()
        .filter_map(|decl| match decl {
            Declaration::Struct(struct_decl) => Some(struct_decl.name.clone()),
            _ => None,
        })
        .collect();
    assert!(
        struct_names.iter().any(|name| name == "Pair__int"),
        "expected instantiated struct, got {:?}",
        struct_names
    );

    let main_func = program
        .declarations
        .iter()
        .find_map(|decl| match decl {
            Declaration::Function(func) if func.name == "main" => Some(func),
            _ => None,
        })
        .expect("main function should exist");

    let body = main_func.body.as_ref().expect("main should have body");
    match &body.statements[0] {
        Statement::VariableDecl(var_decl) => {
            let var_type = var_decl.var_type.as_ref().expect("var type expected");
            match var_type {
                Type::Struct(struct_type) => {
                    assert_eq!(struct_type.name, "Pair__int");
                    assert!(struct_type.type_args.is_empty());
                }
                other => panic!("expected struct type, got {:?}", other),
            }
            if let Some(Expression::Struct(struct_expr)) = &var_decl.initializer {
                match &struct_expr.struct_type {
                    Type::Struct(struct_type) => {
                        assert_eq!(struct_type.name, "Pair__int");
                        assert!(struct_type.type_args.is_empty());
                    }
                    other => panic!("expected struct literal type, got {:?}", other),
                }
            } else {
                panic!("expected struct literal initializer");
            }
        }
        other => panic!("expected variable declaration, got {:?}", other),
    }
}

#[test]
fn test_parse_simple_struct_literal() {
    let source = r#"
        type Pair<T> struct { value T }

        func main() {
            var pair Pair<int> = Pair<int>{ value: 1 }
        }
    "#;
    let mut parser = nrc::parser::Parser::new(source.to_string(), None);
    parser
        .parse_program()
        .expect("parser should accept struct literal");
}

#[test]
fn test_semantic_pass_on_generic_runner() {
    use nrc::semantic::SemanticAnalyzer;

    let source = include_str!("../examples/generic_test.nr");
    let runner = format!(
        "{}\nfunc main() int {{\n    test_generic_demo();\n    return 0;\n}}\n",
        source
    );
    let mut parser = nrc::parser::Parser::new(runner.clone(), Some("runner".into()));
    let mut program = parser.parse_program().expect("parser should succeed");

    if let Some(nrc::ast::Declaration::Struct(struct_decl)) = program.declarations.first() {
        match &struct_decl.fields[0].field_type {
            nrc::ast::types::Type::Generic(name) => assert_eq!(name, "T"),
            other => panic!("expected generic field type in runner, got {:?}", other),
        }
    }

    let mut analyzer = SemanticAnalyzer::new();
    let result = analyzer.analyze(&mut program);
    assert!(result.is_ok(), "semantic analyzer failed: {:?}", result);
}

#[test]
fn test_monomorphized_generics_are_concrete() {
    use nrc::semantic::SemanticAnalyzer;

    let source = include_str!("../examples/generic_test.nr");
    let runner = format!(
        "{}\nfunc main() int {{\n    test_generic_demo();\n    return 0;\n}}\n",
        source
    );
    let mut parser = nrc::parser::Parser::new(runner.clone(), Some("runner".into()));
    let mut program = parser.parse_program().expect("parser should succeed");

    let mut generic_names = std::collections::HashSet::new();
    for declaration in &program.declarations {
        match declaration {
            nrc::ast::Declaration::Function(func) => {
                for generic in &func.generics {
                    generic_names.insert(generic.name.clone());
                }
            }
            nrc::ast::Declaration::Struct(struct_decl) => {
                for generic in &struct_decl.generics {
                    generic_names.insert(generic.name.clone());
                }
            }
            _ => {}
        }
    }

    let mut analyzer = SemanticAnalyzer::new();
    analyzer
        .analyze(&mut program)
        .expect("semantic analysis should succeed");

    nrc::generic::monomorphize_program(&mut program)
        .expect("monomorphization should succeed");

    use nrc::ast::types::Type;
    fn type_refs_generics(ty: &Type, names: &std::collections::HashSet<String>) -> bool {
        match ty {
            Type::Generic(name) => names.contains(name),
            Type::Array(array_type) => type_refs_generics(&array_type.element_type, names),
            Type::Map(map_type) => {
                type_refs_generics(&map_type.key_type, names)
                    || type_refs_generics(&map_type.value_type, names)
            }
            Type::Tuple(tuple_type) => tuple_type
                .element_types
                .iter()
                .any(|t| type_refs_generics(t, names)),
            Type::Struct(struct_type) => {
                names.contains(&struct_type.name)
                    || struct_type
                        .type_args
                        .iter()
                        .any(|t| type_refs_generics(t, names))
            }
            Type::Enum(enum_type) => enum_type
                .type_args
                .iter()
                .any(|t| type_refs_generics(t, names)),
            Type::Function(function_type) => {
                function_type
                    .parameter_types
                    .iter()
                    .any(|t| type_refs_generics(t, names))
                    || function_type
                        .return_type
                        .as_ref()
                        .map_or(false, |t| type_refs_generics(t, names))
            }
            Type::Reference(reference_type) => {
                type_refs_generics(&reference_type.referenced_type, names)
            }
            Type::Vec(vec_type) => type_refs_generics(&vec_type.element_type, names),
            Type::Channel(channel_type) => type_refs_generics(&channel_type.element_type, names),
            Type::Optional(optional_type) => type_refs_generics(&optional_type.inner_type, names),
            Type::Error(error_type) => type_refs_generics(&error_type.inner_type, names),
            Type::Result(result_type) => {
                type_refs_generics(&result_type.ok_type, names)
                    || type_refs_generics(&result_type.err_type, names)
            }
            Type::Option(option_type) => type_refs_generics(&option_type.some_type, names),
            Type::Union(union_type) => union_type
                .member_types
                .iter()
                .any(|t| type_refs_generics(t, names)),
            Type::Alias(alias_type) => {
                names.contains(&alias_type.name)
                    || alias_type
                        .type_args
                        .iter()
                        .any(|t| type_refs_generics(t, names))
            }
            Type::Rc(rc_type) => type_refs_generics(&rc_type.inner_type, names),
            Type::Weak(weak_type) => type_refs_generics(&weak_type.inner_type, names),
            Type::Basic(_) => false,
        }
    }

    for declaration in &program.declarations {
        match declaration {
            nrc::ast::Declaration::Struct(struct_decl) if struct_decl.generics.is_empty() => {
                for field in &struct_decl.fields {
                    assert!(
                        !type_refs_generics(&field.field_type, &generic_names),
                        "Struct {} field {} still references generics: {:?}",
                        struct_decl.name,
                        field.name,
                        field.field_type
                    );
                }
            }
            nrc::ast::Declaration::Function(func) if func.generics.is_empty() => {
                for param in &func.parameters {
                    assert!(
                        !type_refs_generics(&param.param_type, &generic_names),
                        "Function {} parameter {} still references generics: {:?}",
                        func.name,
                        param.name,
                        param.param_type
                    );
                }
                if let Some(return_type) = &func.return_type {
                    assert!(
                        !type_refs_generics(return_type, &generic_names),
                        "Function {} return type still references generics: {:?}",
                        func.name,
                        return_type
                    );
                }
            }
            _ => {}
        }
    }
}

#[test]
fn test_monomorphize_program_creates_instantiation() {
    use nrc::ast::{Declaration, Expression, Statement};

    let source = r#"
        func foo<T>(value T) T {
            return value
        }

        func main() {
            foo<int>(1)
        }
    "#;

    let mut parser = nrc::parser::Parser::new(source.to_string(), None);
    let mut program = parser.parse_program().expect("program should parse");

    generic::monomorphize_program(&mut program).expect("monomorphization should succeed");

    let decl_names: Vec<String> = program
        .declarations
        .iter()
        .map(|decl| decl.name().to_string())
        .collect();

    let instantiated_name = decl_names
        .iter()
        .find(|name| name.starts_with("foo__"))
        .unwrap_or_else(|| {
            panic!(
                "expected monomorphic foo instantiation, declarations: {:?}",
                decl_names
            )
        });
    assert_eq!(instantiated_name, "foo__int");

    let main_func = program
        .declarations
        .iter()
        .find_map(|decl| match decl {
            Declaration::Function(func) if func.name == "main" => Some(func),
            _ => None,
        })
        .expect("main function should exist");

    let body = main_func.body.as_ref().expect("main should have body");
    assert_eq!(body.statements.len(), 1);

    match &body.statements[0] {
        Statement::Expression(Expression::Call(call)) => {
            assert!(call.type_args.is_empty());
            match call.callee.as_ref() {
                Expression::Variable(name) => assert_eq!(name, "foo__int"),
                other => panic!(
                    "call should reference instantiated function, got {:?}",
                    other
                ),
            }
        }
        other => panic!("expected call statement, got {:?}", other),
    }
}
